
#include "Food.h"
#include "SB.h"
#include "InputManager.h"
#include "LevelManager.h"
#include "hRandom.h"
#include "hMath.h"
#include "AudioUtils.h"
#include "ParticleManager.h"
#include "ProgressManager.h"

#define FOOD_TRAIL LevelManager::instance().getFoodTrail()

Food::Food(std::string _sFood) : AnimatedEntity(_sFood, Vector2(-100, -100)) 
{
    m_fPreenteringTimer = 0.0f;
    m_fEnteringTimer = 0.0f;
    m_fWaitingTimer = 0.0f;
    m_fExitingTimer = 0.0f;
    m_fLaunchedTimer = 0.0f;
    m_bMultiFood = false;
    m_bFoodSpawned = false;
    m_bFirstLaunchedUpdate = true;
    
    setState(Food_PreEntering);
    FOOD_TRAIL->m_bRender = false;
    FOOD_TRAIL->m_bStopSpawning = true;
    
    float fSpeed = LevelManager::instance().getGameSpeed();
    setLivingSpeed(Vector2(fSpeed, fSpeed));
}

Food::~Food()
{
    FOOD_TRAIL->m_bRender = false;
    FOOD_TRAIL->m_bStopSpawning = true;
}

void Food::disappear()
{
    setState(Food_Disappearing);
}

void Food::updateTrail()
{
    FOOD_TRAIL->position = Vector3(getPosition().x, getPosition().y, 0);
}

// this help margin defines zones that are "smart" and check if one of two babies are the right one for the food that is being launched
// the ranges are defined from ANGLE_TOP_MIN - ANGLE_HELP_MARGIN to ANGLE_TOP_MIN and from ANGLE_TOP_MAX to ANGLE_TOP_MAX + ANGLE_HELP_MARGIN
const float ANGLE_HELP_MARGIN = 5.0f;

const float MIN_SPEED = 1500.0f;
const float MAX_SPEED = 2500.0f;
const float SPEED_MULTIPLIER = 1.5f;

Vector2 Food::getBabyPosition(eBabyPos ePos)
{
    switch(ePos)
    {
        case BabyLeft:
            return Vector2(60.0f, 250.0f);
        case BabyCenter:
            return Vector2(160.0f, 350.0f);
        case BabyRight:
            return Vector2(260.0f, 250.0f);
        case BabyDiscard:
            return Vector2(160.0f, 10.0f);
    }
    return Vector2(0.0f,0.0f);
}

const float BABY_ZONE_RADIUS = 65.0f;
void Food::checkLaunched()
{
    // if a prize was took this frame don't check the input for the food throwing this frame
    if (LevelManager::instance().getPrizeTookThisFrame()) return;
    
    bool bDiscarded = false;
    bool bLaunched = false;
        
    // CHECK IF PLAYER MOVES FAST TO A BABY
    Vector2 vSpeed = InputManager::instance().getSpeed();
    // get the length of the speed
    m_fSpeed = vSpeed.length() * SPEED_MULTIPLIER;
    
    /*if( m_fSpeed > 500.0f)
    {
        if (m_fSpeed < MIN_SPEED) m_fSpeed = MIN_SPEED;
        m_vDirection = vSpeed;
        m_vDirection.normalize();
        float fAngle = hMath::getAngleOfDirection(m_vDirection) * RadiansToDegreesFactor;
        
        // limit the max speed of the food
        if (m_fSpeed > MAX_SPEED)
        {
            m_fSpeed = MAX_SPEED;
        }
        
        // first of all, see if the player has launched in the direction of the smart zones...
        if ((fAngle <= ANGLE_TOP_MIN && fAngle >= ANGLE_TOP_MIN - ANGLE_HELP_MARGIN) ||
            (fAngle <= ANGLE_TOP_MAX + ANGLE_HELP_MARGIN && fAngle >= ANGLE_TOP_MAX))
        {
            if (LevelManager::instance().babyAndFoodMatch(LevelManager::instance().getBabyInPos(BabyCenter)->entityName, entityName))
            {
                launch(BabyCenter);
                bLaunched = true;
            }
        }
        
        if (!bLaunched)
        {
            if(fAngle > ANGLE_TOP_MIN && fAngle < ANGLE_TOP_MAX)
            {
                launch(BabyCenter);
                // apply center speed modifier
                m_fSpeed *= 1.2f;
                bLaunched = true;
            }
            else if( vSpeed.y < 0.0f)
            {
                bDiscarded = true;
                bLaunched = true;
            }
            else if( vSpeed.x < 0.0f)
            {
                launch(BabyLeft);
                bLaunched = true;
            }
            else if( vSpeed.x > 0.0f)
            {
                launch(BabyRight);
                bLaunched = true;
            }
        }
    }*/
    
    //Check motion launch
    if(ProgressManager::instance().getSavedData().bAccelerometer)
    {
       switch(InputManager::instance().getMotionDirection())
        {
            case MOTION_DOWN:
                m_fSpeed = 2500.0f;
                m_vDirection = Vector2(0, -1);
                launch(BabyDiscard);
                LevelManager::instance().discardedFood(entityName);
                break;
                
            case MOTION_UP:
                m_fSpeed = 2500.0f;
                m_vDirection = Vector2(0, 1);
                launch(BabyCenter);
                bLaunched = true;
                break;
                
            case MOTION_LEFT:
                m_fSpeed = 2500.0f;
                m_vDirection = Vector2(-1, 0.5);
                m_vDirection.normalize();
                launch(BabyLeft);
                bLaunched = true;
                break;
                
            case MOTION_RIGHT:
                m_fSpeed = 2500.0f;
                m_vDirection = Vector2(1, 0.5);
                m_vDirection.normalize();
                launch(BabyRight);
                bLaunched = true;
                break;
                
            case MOTION_NONE:
                break;
        }
    }
    
    // CHECK IF PLAYER CLICKS ON A BABY ZONE
    Vector2 vTrazo = InputManager::instance().getPosition() - InputManager::instance().getLastJustPressedPosition();
    if (InputManager::instance().hasJustReleased() && !bLaunched && vTrazo.length() < 20.0f)
    {
        Vector2 vPos = InputManager::instance().getPosition();
        m_fSpeed = 2500.0f;
        
        if( (vPos - getBabyPosition(BabyLeft)).length() < BABY_ZONE_RADIUS )
        {
            launch(BabyLeft);
            bLaunched = true;
        }
        /*else if( (vPos - getBabyPosition(BabyDiscard)).length() < BABY_ZONE_RADIUS )
         {
         bDiscarded = true;
         bLaunched = true;
         }*/
        else if( (vPos - getBabyPosition(BabyCenter)).length() < BABY_ZONE_RADIUS )            
        {
            launch(BabyCenter);
            bLaunched = true;
        }
        else if( (vPos - getBabyPosition(BabyRight)).length() < BABY_ZONE_RADIUS )
        {
            launch(BabyRight);
            bLaunched = true;
        }
        if (bLaunched)
        {
            // TODO depending on resolution this thing will not work...
            m_vDirection = InputManager::instance().getPosition() - getPosition() * 0.5f;
            m_vDirection.normalize();
        }
    }
    
    // IF PLAYER RELEASES AFTER DESCRIBING A DIRECTION
    if (!bLaunched && InputManager::instance().hasJustReleased())
    {
        if (m_bMultiFood) // in multifood, check only if the player started drawing over this food
        {
            float length = (InputManager::instance().getLastJustPressedPosition() - getPosition() * 0.5f).length();
            if (length > 30.0f)
                return;
        }
        
        Vector2 vJustPressedPosition = InputManager::instance().getLastJustPressedPosition();
        float fTime = InputManager::instance().getTimeSinceLastJustPressed();
        m_vDirection = InputManager::instance().getPosition() - vJustPressedPosition;
        float fLength = m_vDirection.length();
        if (fLength > 10.0f)
        {
            m_fSpeed = fLength / fTime;
            m_fSpeed *= SPEED_MULTIPLIER;
            m_vDirection.normalize();
            
            float fAngle = hMath::getAngleOfDirection(m_vDirection) * RadiansToDegreesFactor;
            // angle of the point between babies to see in which zone will fall
            
            float fAngleMin = hMath::getAngleOfDirection(Vector2(110.0f, 260.0f) - vJustPressedPosition) * RadiansToDegreesFactor;
            float fAngleMax = hMath::getAngleOfDirection(Vector2(210.0f, 260.0f) - vJustPressedPosition) * RadiansToDegreesFactor;
            
            // limit the min and max speed of the food
            if (m_fSpeed < MIN_SPEED) m_fSpeed = MIN_SPEED;
            if (m_fSpeed > MAX_SPEED) m_fSpeed = MAX_SPEED;
            
            //vSpeed = m_vDirection * m_fSpeed;
            
            // first of all, see if the player has launched in the direction of the smart zones...
            if ((fAngle <= fAngleMin && fAngle >= fAngleMin - ANGLE_HELP_MARGIN) ||
                (fAngle <= fAngleMax + ANGLE_HELP_MARGIN && fAngle >= fAngleMax))
            {
                if (LevelManager::instance().babyAndFoodMatch(LevelManager::instance().getBabyInPos(BabyCenter)->entityName, entityName))
                {
                    launch(BabyCenter);
                    bLaunched = true;
                }
            }
            
            if (!bLaunched)
            {
                if (m_vDirection.y < 0.0f)
                {
                    bDiscarded = true;
                    bLaunched = true;
                }
                else if(fAngle < fAngleMin && fAngle > fAngleMax)
                {
                    launch(BabyCenter);
                    // apply center speed modifier
                    m_fSpeed *= 1.2f;
                    bLaunched = true;
                }
                else if( fAngle < fAngleMax )
                {
                    launch(BabyRight);
                    bLaunched = true;
                }
                else if( fAngle > fAngleMin )
                {
                    launch(BabyLeft);
                    bLaunched = true;
                }
            }
        }
    }
        
    // when discarding food, it can be done with less speed
    if (bDiscarded || (m_fSpeed > 50.0f && vSpeed.y < 0.0f))
    {
        if (m_fSpeed < 500.0f) m_fSpeed = 500.0f;
        launch(BabyDiscard);
        LevelManager::instance().discardedFood(entityName);
    }
    
    if (bLaunched)
    {
        float fRand = hRandom::getRandom();
        if(fRand < 0.5)
            AudioUtils::playSfx("FX_Launch2.mp3");
        else
            AudioUtils::playSfx("FX_Launch3.mp3");
    }
}


void Food::update()
{
    AnimatedEntity::update();
    
    float fGameSpeed = LevelManager::instance().getGameSpeed();
    
	float fPerc;
    
    switch(m_eFoodState)
	{
        case Food_PreEntering:
        {
            updateTrail();
            
            m_fPreenteringTimer += SB::dt * fGameSpeed;
            if (m_fPreenteringTimer > FOOD_PREENTER_TIME)
            {
                setState(Food_Entering);
            }
            break;
        }
		case Food_Entering:
        {
            updateTrail();
            
            m_fEnteringTimer += SB::dt * fGameSpeed;
            fPerc = m_fEnteringTimer / FOOD_ENTER_TIME; 
            
			//SetPosition
            float x = m_vPosInit.x + (m_vPosEnd.x - m_vPosInit.x) * fPerc;
            float y = m_vPosInit.y + (m_vPosEnd.y - m_vPosInit.y) * sin(fPerc * PiOver2);
			setPosition(Vector2(x, y));
            //setScale(m_vScaleEnd * fPerc);
            
			if(fPerc >= 1)
			{
				setState(Food_Waiting);
			}
			
			break;
        }
            
		case Food_Waiting:
        {
            updateTrail();
            
            int iTurns = LevelManager::instance().getScore();
            float fWaitingTime;
            if (iTurns < 3)
            {
                fWaitingTime = 4.0f / LevelManager::instance().getGameSpeed();
            }
            else if (iTurns < 6)
            {
                fWaitingTime = 3.0f / LevelManager::instance().getGameSpeed();
            }
            else
            {
                 fWaitingTime = 2.0f / LevelManager::instance().getGameSpeed();
            }
            
            m_fWaitingTimer += SB::dt * fGameSpeed;
            fPerc = m_fWaitingTimer / fWaitingTime; 
            
            //SetPosition
            float x = m_vPosInit.x + (m_vPosEnd.x - m_vPosInit.x) * fPerc;
            float y = m_vPosInit.y + (m_vPosEnd.y - m_vPosInit.y) * fPerc;
			setPosition(Vector2(x, y));
            
            checkLaunched();

            if(getState() == Food_Waiting && fPerc > 1.0f)
            {
                setState(Food_Exiting);
            }
            
			break;
        }
		case Food_Launched:
		{
            updateTrail();
            
            float fLaunchSpeed = (400.0f * 0.5f) + (m_fSpeed * 0.5f);
            float fOffsetDuration = 0.4f;
            // update the launch timer
            m_fLaunchedTimer += SB::dt;
            float fDirectionOffsetFactor = 1 - (m_fLaunchedTimer / fOffsetDuration);
            
			fPerc = (SB::gameTime - m_fTimeEnterState) / FOOD_LAUNCH_TIME; 
			
			// apply the movement
            Baby* oBaby = LevelManager::instance().getBabyInPos(m_eBabyPos);
            Vector2 vMouthPos = Vector2(0.0f, 0.0f);
            // sometimes in iPhone oBaby is null... :S
            if (oBaby != NULL)
            {
                vMouthPos = oBaby->getMouthPos();
            }
            Vector2 vDirection;
            if (m_eBabyPos == BabyDiscard)
            {
                vDirection = Vector2(0.0f, -1.0f);
                setPosition( getPosition() + (vDirection * SB::dt * fLaunchSpeed));
            }
            else
            {
                vDirection = vMouthPos - getPosition();
                vDirection.normalize();
                vDirection = vDirection * (1 - fDirectionOffsetFactor) + m_vDirection * fDirectionOffsetFactor;
                setPosition( getPosition() + (vDirection * SB::dt * fLaunchSpeed));
            }
            
            if(oBaby && oBaby->waitingOrEating())
            {
                // first update
                if(m_bFirstLaunchedUpdate /*&& LevelManager::instance().babyAndFoodMatch(oBaby->getEntityName(), entityName)*/)
                {
                    m_bFirstLaunchedUpdate = false;
                    LevelManager::instance().updateAvailableBabies();
                    LevelManager::instance().setNextBabyType(oBaby->getBabyPos());
                }
                
                float fDistanceWithBaby = (oBaby->getMouthPos() - getPosition()).length();
                
                // update scale
                float fScaleFactor = fDistanceWithBaby / 250.0f;
                fScaleFactor = (fScaleFactor * 0.5f) + 0.4f;
                if (fScaleFactor < 1.0f)
                    setScale(Vector2(fScaleFactor, fScaleFactor));
                
                // play the eating anim before the food arrives
                if (fDistanceWithBaby < 250 && oBaby->getBabyState() == Baby_Waiting)
                {
                    if(LevelManager::instance().babyAndFoodMatch(oBaby->getEntityName(), entityName))
                    {
                        oBaby->setBabyState(Baby_EatSuccess);
                    }
                    else
                    {
                        oBaby->setBabyState(Baby_EatFail);
                    }
                    vDirection.normalize();
                    oBaby->setHitDirection(vDirection * 20);
                }
                // end the food
                if(fDistanceWithBaby < 25)
                {
                    setState(Food_End);
                }
            }
            else
            {
                if (m_fLaunchedTimer * fGameSpeed > 0.6f)
                {
                    setState(Food_End);
                }
            }

			break;
		}
		case Food_Exiting:
        {
            updateTrail();
            
            m_fExitingTimer += SB::dt * fGameSpeed;
            fPerc = m_fExitingTimer / FOOD_EXIT_TIME; 
            
            if (fPerc < 0.7f)
            {
                checkLaunched();
            }
            
			if(fPerc >= 1.0)
			{
				setState(Food_End);
			}
			
            //SetPosition
            float x = m_vPosInit.x + (m_vPosEnd.x - m_vPosInit.x) * fPerc;
            float y = m_vPosInit.y + (m_vPosEnd.y - m_vPosInit.y) * (sin(fPerc * PiOver2 - PiOver2) + 1);
			setPosition(Vector2(x, y));

            //setScale(m_vScaleInit + m_vScaleEnd * fPerc);
			break;
        }
        case Food_Disappearing:
            m_fExitingTimer += SB::dt * fGameSpeed;
            if (m_fExitingTimer > 1.0f)
            {
                m_fExitingTimer = 1.0f;
                setState(Food_End);
            }
            setScale(Vector2(1.0f - m_fExitingTimer, 1.0f - m_fExitingTimer));
            break;
		case Food_End:
			break;
	}
}

void Food::setState(eFoodState _iState)
{
	m_eFoodState = _iState;
	m_fTimeEnterState = SB::gameTime;
    
    switch(m_eFoodState)
    {
        case Food_Entering:
        {
            if (m_bMultiFood)
            {
                if (m_iMultiPosition == 1)
                {
                    m_vPosInit = Vector2(540.0f, -100.0f);
                    m_vPosEnd = Vector2(200.0f, 140.0f);
                }
                else if (m_iMultiPosition == 2)
                {
                    m_vPosInit = Vector2(540.0f, -100.0f);
                    m_vPosEnd = Vector2(380.0f, 140.0f);
                }
                else
                {
                    m_vPosInit = Vector2(540.0f, -100.0f);
                    m_vPosEnd = Vector2(560.0f, 140.0f);
                }
            }
            else
            {
                m_vPosInit = Vector2(540.0f, -100.0f);
                m_vPosEnd = Vector2(380.0f, 140.0f);
            }
            m_vScaleEnd = Vector2(0.5f, 0.5f);
            break;
        }
        case Food_Waiting:
            m_vPosInit = getPosition();
            if (m_bMultiFood)
            {
                m_vPosEnd = getPosition() + Vector2(-40.0f, 0.0f);
            }
            else
            {
                m_vPosEnd = Vector2(260.0f, 140.0f);
            }
            
            m_vScaleInit = Vector2(0.5, 0.5);
            m_vScaleEnd = Vector2(1.2, 1.2);
            break;
        case Food_Launched:
            updateTrail();
            FOOD_TRAIL->resetParticles();
            break;
        case Food_Exiting:
            LevelManager::instance().foodGone(entityName);

            m_vPosInit = getPosition();
            m_vPosEnd = Vector2(100.0f, -100.0f);
            m_vScaleInit = Vector2(0.5, 0.5);
            m_vScaleEnd = Vector2(1.2, 1.2);
            break;
        case Food_Disappearing:
            m_fExitingTimer = 0.0f;
            break;
        case Food_End:
            if (LevelManager::instance().isThreeBabiesRound())
            {
                LevelManager::instance().setEndSpecialRound();
            }
            else
            {
                LevelManager::instance().addRound();
            }
            break;
        default:
            break;
    }
}

void Food::launch(eBabyPos _iBabyPos)
{
	m_vPosInit = m_vPosition;

    m_eBabyPos = _iBabyPos;
    
    if(_iBabyPos == BabyDiscard)
    {
        m_vPosEnd = m_vPosInit - Vector2(0, 200);
        m_vDirection = Vector2(0,-1.0f);
        m_fSpeed = 1000.0f;
    }
    else if (LevelManager::instance().getBabyInPos(m_eBabyPos) != NULL)
    {
        m_vPosEnd = LevelManager::instance().getBabyInPos(m_eBabyPos)->getMouthPos();
	}

    setState(Food_Launched);
}
